<cfscript>
component extends="SimpleDBObject" implements="ItemInterface" {

	function init(required sdb, required string domain, required string itemName) {
		variables.sdb = sdb; // connection to sdb
		variables.domain = domain;
		variables.itemName = itemName;
		super.init();
		return this;
	}

	function getItemName() {
		return variables.itemName;
	}

	function get(required string key) {
		local.result = variables.sdb.doSync("getAttributes", _jobj("com.amazonaws.services.simpledb.model.GetAttributesRequest").init(variables.domain, getItemName()).withAttributeNames([key])).getAttributes();
		local.value = _toData(local.result);
		return StructKeyExists(local.value, key)?local.value[key]:"";
	}
		
	function set(required string key, required value) {
		local.value = _asArray(value);
		local.attrs = []
		loop array="#local.value#" index="local.i" {
			ArrayAppend(local.attrs, _toReplaceableAttribute(key, local.i, true));
		}
		_putAttributes(local.attrs);
	}
		
	function has(required string key) {
		var value = get(key);
		return IsArray(value) or Len(value);
	}
	
	function remove(string key) {
		local.attrs = [];
		loop array="#_asArray(get(key))#" index="local.i" {
			ArrayAppend(local.attrs, _toAttribute(key, local.i));
		}
		_removeAttributes(local.attrs);
	}
		
	function delete() {
		local.request = _jobj("com.amazonaws.services.simpledb.model.DeleteAttributesRequest").init(variables.domain, getItemName());
		variables.sdb.do("deleteAttributes", local.request);
	}
	
	function exists() {
		var data = fetch(true);
		return StructCount(data) gte 1;
	}
	
	function fetch(boolean consistent=false) {
		local.result = variables.sdb.doSync("getAttributes", _jobj("com.amazonaws.services.simpledb.model.GetAttributesRequest").init(variables.domain, getItemName()).withConsistentRead(consistent));
		return _toData(local.result.getAttributes());
	}
	
	function toMemento() {
		if (ArrayLen(arguments) gte 1) local.data = arguments[1];
		else local.data = fetch();
		return {name:getItemName(), data:data};
	}
		
	/* Private functions */
	
	private function _putAttributes(array attrs) {
		local.request = _jobj("com.amazonaws.services.simpledb.model.PutAttributesRequest").init(variables.domain, getItemName(), attrs);
		variables.sdb.do("putAttributes", local.request);		
	}
	
	private function _removeAttributes(array attrs) {
		local.request = _jobj("com.amazonaws.services.simpledb.model.DeleteAttributesRequest").init(variables.domain, getItemName(), attrs);
		variables.sdb.do("deleteAttributes", local.request);
	}

	private function _toReplaceableAttribute(string key, value, repl) {
		return _jobj("com.amazonaws.services.simpledb.model.ReplaceableAttribute").init(key, value, repl);
	}
	
	private function _toAttribute(key, value) {
		return _jobj("com.amazonaws.services.simpledb.model.Attribute").init(key, value);
	}
	
	private function _toData(array attrs, struct data={}) {
		loop array="#attrs#" index="local.attr" {
			if (StructKeyExists(data, local.attr.getName())) {
				data[local.attr.getName()] = _asArray(data[local.attr.getName()]);
				ArrayAppend(data[local.attr.getName()], local.attr.getValue());
			} else { 
				data[local.attr.getName()] = local.attr.getValue();
			}
		}
		return data;
	}
	
	// synthetic getter and setters
	function onMissingMethod(string name, struct args) {
		
		local.wasSetter = false;
	 	if (_isSetterCall(name, args)) {
			local.key = _getKeynameFromGetterSetterCall(name);
			local.value = args[1];
			local.wasSetter = true;
			set(local.key, local.value);	
		} else if (_isGetterCall(name)) {
			local.key = _getKeynameFromGetterSetterCall(name);
			return get(local.key);	
		} else if (_isHasCall(name)) {
			local.key = _getKeynameFromGetterSetterCall(name);
			return has(local.key);
		}

		if (not local.wasSetter) 
			//return variables.obj[name](argumentcollection=args);
			throw(type="Application", message="Method with name #name.toUpperCase()# not found.");
	}

	private function _isSetterCall(name, args) {
		return (Len(name) gt 3) and (Left(name, 3) eq "set") and (StructCount(args) eq 1);
	}

	private function _isGetterCall(name) {
		return (Len(name) gt 3) and Left(name, 3) eq "get";
	}
	
	private function _isHasCall(name) {
		return (Len(name) gt 3) and Left(name, 3) eq "has";		
	}

	private function _getKeynameFromGetterSetterCall(name) {
		return Right(name, Len(name)-3).toLowerCase();
	}

	
	
}
</cfscript>